import logging
import os
import os.path
import socket
import subprocess
import time

from selenium import webdriver
from selenium.webdriver.chrome.options import Options

from config.config import ChromeConfig, SeleniumConfig

"""
    通过远程的方式打开chrome浏览器，然后通过selenium接管浏览器
    不会被检测到是自动化操作
"""


class BrowserDev:
    def __init__(self, PORT=9093):
        self.log = logging.getLogger(__name__)  # 日志对象
        self._browser = None  # 浏览器对象
        self.PORT = PORT  # 调试端口

        # 设置Chrome调试模式的数据存放路径 chromeDataPathAndPrefix + phoneNumber 即为实际路径
        self.chromeDataPathAndPrefix = ChromeConfig.chromeDataPathAndPrefix
        # chrome浏览器驱动位置
        self.chrome_path = ChromeConfig.chrome_path

    def init(self):
        """
        初始化chrome对象
        :return: 浏览器对象
        """
        if not self.port_opened() and not self._launch():
            raise Exception("启动浏览器失败")
        self.attach()
        return self._browser

    def port_opened(self):
        """
        :return: 检测浏览器调试端口是否开启
        """
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        try:
            s.connect(('127.0.0.1', self.PORT))
            # self.log.info(f'端口：{self.PORT} 已开启')
            s.close()
            return True
        except socket.error as e:
            # self.log.warning(f'端口：{self.PORT} 未开启')
            return False

    def close(self):
        """
        浏览器调试端口若是开启状态,则先关闭这个浏览器
        """
        print("close chrome")
        if not self.port_opened(): return  # 如果浏览器已经关闭（在对应监控上没有检测到），则不需要再关闭
        self.attach()
        if self._browser is None: return  # 如果浏览器已经关闭，则不需要再关闭
        while self.port_opened():  # 确保浏览器已经关闭
            try:
                # 关闭所有标签页，浏览器就自动关闭了
                while len(self._browser.window_handles) >= 1:
                    self._browser.switch_to.window(self._browser.window_handles[-1])
                    self._browser.close()
            except Exception as e:
                pass
            time.sleep(0.1)
        # self._browser.quit() # 按理这种方式是最好的，但是无效
        self._browser = None
        self.log.info('浏览器已关闭')

    def attach(self):
        """
        使用 selenium 接管已经打开的浏览器
        :return: 浏览器对象
        """
        options = Options()
        options.add_experimental_option("debuggerAddress", f"127.0.0.1:{self.PORT}")

        # keep_alive=False 解决urllib3.connectionpool 警告  https://www.cnblogs.com/xingyaowuhen/p/17868064.html
        self._browser = webdriver.Chrome(options=options, keep_alive=False)

    def _launch(self):
        """
        启动浏览器
        """
        if not os.path.exists(self.chrome_path):
            self.log.critical('启动程序错误，没有正确安装Chrome浏览器')
            return False
        chromeDataPathAndPrefix = os.path.join(os.getcwd(), self.chromeDataPathAndPrefix)
        chromeDataPathAndPrefix = os.path.abspath(chromeDataPathAndPrefix)

        os.makedirs(chromeDataPathAndPrefix, exist_ok=True)
        """
        chrome_path: chrome浏览器驱动位置
        --remote-debugging-port: 调试端口
        --user-data-dir: 用户数据目录
        --no-first-run: 不是第一次运行, 不会弹出欢迎界面
        --no-default-browser-check: 不检查是否是默认浏览器
        """
        # 通过子进程模式启动Chrome浏览器
        chrome_command = f'"{self.chrome_path}" --remote-debugging-port={self.PORT} --user-data-dir="{chromeDataPathAndPrefix}" --no-first-run --no-default-browser-check'

        # 若是监控模式 则禁用图片加载
        # if self.isMonitor: chrome_command += " --disable-gpu --blink-settings=imagesEnabled=false"
        subprocess.Popen(chrome_command)

        s = time.time()
        while time.time() - s < SeleniumConfig.browserStart:
            if self.port_opened():
                self.log.info('已成功启动Chrome浏览器')
                return True
            time.sleep(SeleniumConfig.interval)

        self.log.critical('启动浏览器失败：已超时')
        return False
